package top.codetheory.thread._002_保护性暂停;

import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author: CodeTheory
 * @since: 2024/4/24 下午6:04
 **/
@Slf4j
public class _001_等待线程执行_送信收信 {

    public static void main(String[] args) {
        test2();
    }

    /** 可以看到 t1 线程等待 t2 线程执行完毕后才能继续执行 <br>
     * <img src="https://gitee.com/codetheory/img-on1/raw/master/img01/1714039501270-2024-4-2518:05:02.png"  />
     */
    public static void test1() {
        GuardedObject guarded = new GuardedObject();
        new Thread(() -> {
            Object response = guarded.getResponse();
            log.info("收到结果:{}", response);
        }, "t1").start();

        try {
            log.info("睡眠两秒");
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        new Thread(() -> {
            guarded.complete("你好");
        }, "t2").start();

        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * 可以看到, 中途唤醒了t1, 但是他总共等待了2s, 符合预期<br>
     * <img src="https://gitee.com/codetheory/img-on1/raw/master/img01/1714040755788-2024-4-2518:25:56.png"  />
     */
    public static void test2() {
        GuardedObjectWithWaitingTime guarded = new GuardedObjectWithWaitingTime();
        new Thread(() -> {
            Object response = guarded.getResponse(2000);
            log.info("收到结果:{}", response);
        }, "t1").start();

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        guarded.complete(null);

        new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            guarded.complete("你好");
        }, "t2").start();

        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

    }


}

/**
 * 守卫对象
 */
class GuardedObject {
    private static final Logger log = LoggerFactory.getLogger(GuardedObject.class);
    private Object response;
    private final Object lock = new Object();

    public Object getResponse() {
        synchronized (lock) {  // wait 需要加锁
            while (response == null) {  // 使用 while 防止虚假唤醒
                try {
                    log.info("等待中");
                    lock.wait();
                    log.info("唤醒了");
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            // 不能直接返回, 需要等待结果输出之后才能返回, 所以上面要加wait
            log.info("返回结果, {}", response);
            return response;
        }
    }

    public void complete(Object response) {
        // 使用锁, 因为这里操作共享变量了, 会有线程安全问题
        synchronized (lock) {
            log.info("获取到锁, 赋值:{}", response);
            this.response = response;
            log.info("唤醒所有等待的线程");
            lock.notifyAll(); // 唤醒所有等待的线程
        }
    }

}


/**
 * 带超时时间的守卫对象
 */
class GuardedObjectWithWaitingTime {
    private static final Logger log = LoggerFactory.getLogger(GuardedObjectWithWaitingTime.class);
    private Object response;
    private final Object lock = new Object();

    public Object getResponse(long millis) {
        synchronized (lock) {  // wait 需要加锁
            long begin = System.currentTimeMillis();
            while (response == null) {  // 使用 while 防止虚假唤醒

                if (millis <= 0) {  // 如果剩余等待时间小于等于0, 直接返回
                    break;
                }

                try {
                    log.info("等待中");
                    lock.wait(millis);  // 需要计算剩余等待时间, 不然虚假唤醒的话, 会再等待 millis 时间
                    log.info("唤醒了");
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                millis -= System.currentTimeMillis() - begin;  // 计算剩余等待时间
            }
            // 不能直接返回, 需要等待结果输出之后才能返回, 所以上面要加wait
            log.info("返回结果, {}", response);
            return response;
        }
    }

    public void complete(Object response) {
        // 使用锁, 因为这里操作共享变量了, 会有线程安全问题
        synchronized (lock) {
            log.info("获取到锁, 赋值:{}", response);
            this.response = response;
            log.info("唤醒所有等待的线程");
            lock.notifyAll(); // 唤醒所有等待的线程
        }
    }

}